package Modellus.WinUI.ModellusFrame;

import Modellus.Core.IPersister;
import Modellus.Core.Model.IModel;
import Modellus.Core.Model.XmlUtils;
import Modellus.Core.PersistableHelper;
import Modellus.Core.PersistanceData;
import Modellus.WinUI.Commands.*;
import Modellus.WinUI.Editor.EditorInternalFrame;
import Modellus.WinUI.Grid.ITableInternalFrame;
import Modellus.WinUI.Grid.TableInternalFrame;
import Modellus.WinUI.InternalFrames.*;
import Modellus.WinUI.Resource.Home.HomeRibbonTaskResource;
import Modellus.WinUI.Resource.ModellusIcon.ModellusIconResource;
import Modellus.WinUI.Utils.ResizableBufferedImage;
import Modellus.WinUI.Utils.UI.Ribbon.IExtendedJRibbonBand;
import Modellus.WinUI.*;
import Modellus.WinUI.ExampleTable.TableExampleFrame;
import org.jvnet.substance.SubstanceLookAndFeel;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Locale;
import java.util.ResourceBundle;

public class ModellusFrame extends JFrame implements IModellusMainWindow, ActionListener {
    IModellusMainPanel _modellusMainPanel;
    public static final String TITLE_ON_TITLEBAR = "Modellus";
    public static String NEW_DOCUMENT_TITLE;
    public static final String MODELLUS_CONFIGURATION_FILE = "ModellusConfig.xml";
    static ModellusFrame modellusFrame;
    static Document ModellusConfigurationDocument;
    private static PersistableHelper PersistableHelper = new PersistableHelper();

    public ModellusFrame(IModel model, IStatusBar statusBar, IAnimatorInternalFrame animator, IChart2InternalFrame chart, ITableInternalFrame table, IEditorInternalFrame editor, INotesInternalFrame notes, Modellus.WinUI.ExampleTable.ITableInternalFrame inputTable) {
        super(NEW_DOCUMENT_TITLE);
        Rectangle maximumWindowBounds = GraphicsEnvironment.getLocalGraphicsEnvironment().getMaximumWindowBounds();
        if (maximumWindowBounds.width <= 1024 && maximumWindowBounds.height < 768) {
            setPreferredSize(new Dimension(maximumWindowBounds.width, maximumWindowBounds.height));
            setMinimumSize(new Dimension(maximumWindowBounds.width, maximumWindowBounds.height));
        } else {
            setPreferredSize(new Dimension(1024, 768));
            setMinimumSize(new Dimension(1024, 768));
        }
        setLocation(0, 0);
        pack();
        _modellusMainPanel = new ModellusMainPanel(model, statusBar, animator, chart, table, editor, notes, inputTable);
        fillFileBand();
        setContentPane(_modellusMainPanel.getComponentRepresentation());
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                CloseCommand closeCommand = new CloseCommand(ModellusFrame.this, _modellusMainPanel.getPersistanceData());
                closeCommand.execute();
            }
        });    
        setIcon(ModellusIconResource.instance().getModellusEarthIcon());
    }

    private void setIcon(URL iconImage) {
        BufferedImage bufferedImage = null;
        try {
            bufferedImage = ImageIO.read(iconImage);
        }
        catch (IOException ioExpection) {
            throw new RuntimeException("Erro ao ler o ficheiro: " + bufferedImage);
        }
        setIconImage(bufferedImage);
    }

    @Override
    public void setTitle(String title)
    {
        super.setTitle(TITLE_ON_TITLEBAR + " - " + title);
    }

    private void fillFileBand() {
        IExtendedJRibbonBand fileBand = _modellusMainPanel.getRibbon().getHomeRibbonTask().getFileBand();
        PersistanceData persistanceData = _modellusMainPanel.getPersistanceData();
        NewCommand newCommand = new NewCommand(_modellusMainPanel, persistanceData);
        LoadCommand loadCommand = new LoadCommand(_modellusMainPanel, persistanceData);
        SaveAsCommand saveAsCommand = new SaveAsCommand(_modellusMainPanel, persistanceData);
        SaveCommand saveCommand = new SaveCommand(_modellusMainPanel, persistanceData);
        ResourceBundle homeTaskResourceBundle = LocaleInformation.instance().getBundle("Modellus/WinUI/homeTask");
        ResizableBufferedImage resizableBufferedImage = new ResizableBufferedImage(HomeRibbonTaskResource.getInstance().getOpenFileImage(), HomeRibbonTaskResource.getInstance().getOpenFile16Image());
        fileBand.addJRibbonCommandButton(resizableBufferedImage, homeTaskResourceBundle.getString("Open_Button"), loadCommand);
        resizableBufferedImage = new ResizableBufferedImage(HomeRibbonTaskResource.getInstance().getNewImage(), HomeRibbonTaskResource.getInstance().getNew16Image());
        fileBand.addJRibbonCommandButton(resizableBufferedImage, homeTaskResourceBundle.getString("New_Button"), newCommand);
        resizableBufferedImage = new ResizableBufferedImage(HomeRibbonTaskResource.getInstance().getSaveFileImage(), HomeRibbonTaskResource.getInstance().getSaveFile16Image());
        fileBand.addJRibbonCommandButton(resizableBufferedImage, homeTaskResourceBundle.getString("Save_Button"), saveCommand);
        resizableBufferedImage = new ResizableBufferedImage(HomeRibbonTaskResource.getInstance().getSaveAsFileImage(), HomeRibbonTaskResource.getInstance().getSaveAsFile16Image());
        fileBand.addJRibbonCommandButton(resizableBufferedImage, homeTaskResourceBundle.getString("Save_As_Button"), saveAsCommand);
        saveAsCommand.addCommandExecutionListener(new ICommandExecutionListener() {
            public void commandExecuted(ICommand executedCommand) {
                setTitle(_modellusMainPanel.getPersistanceData().getFilePath());
            }
        });

        saveCommand.addCommandExecutionListener(new ICommandExecutionListener() {
            public void commandExecuted(ICommand executedCommand) {
                setTitle(_modellusMainPanel.getPersistanceData().getFilePath());
            }
        });

        newCommand.addCommandExecutionListener(new ICommandExecutionListener() {
            public void commandExecuted(ICommand executedCommand) {
                setTitle(NEW_DOCUMENT_TITLE);
            }
        });
    }

    public void actionPerformed(ActionEvent e) {
        if ("quit".equals(e.getActionCommand())) {
            quit();
        }
    }

    public IModellusMainPanel getModellusMainPanel() {
        return _modellusMainPanel;
    }

    public void quit() {
        saveModellusConfiguration();
        System.exit(0);
    }

    private static void saveModellusConfiguration() {
        String filePath = MODELLUS_CONFIGURATION_FILE;
        XmlUtils xmlUtils = new XmlUtils();
        PersistableHelper persisterHelper = new PersistableHelper();
        Document document = xmlUtils.getEmptyDocument();
        xmlUtils.createRootNode(document, "ModellusConfiguration");
        Node localeNode = persisterHelper.saveHeader(document.getFirstChild(), "LocaleSettings");
        Locale currentLocale = LocaleInformation.instance().getLocale();
        persisterHelper.saveString(localeNode, "Language", currentLocale.getLanguage());
        persisterHelper.saveString(localeNode, "Country", currentLocale.getCountry());
        Node skinNode = persisterHelper.saveHeader(document.getFirstChild(), "SkinInformation");
        String substanceSkinQualifiedName = SubstanceLookAndFeel.getCurrentSkin().getClass().getName();
        persisterHelper.saveString(skinNode, "SkinName", substanceSkinQualifiedName);
        File file = new File(filePath);
        xmlUtils.saveDocument(document, file);
    }

    private static void loadLocaleSettingsConfiguration() {
        if (ModellusConfigurationDocument != null) {
            Node localeSettingsNode = PersistableHelper.loadHeader(ModellusConfigurationDocument.getFirstChild(), "LocaleSettings");
            if (localeSettingsNode != null) {
                String country = PersistableHelper.loadString(localeSettingsNode, "Country");
                String language = PersistableHelper.loadString(localeSettingsNode, "Language");
                Locale locale = new Locale(language, country);
                LocaleInformation.instance().setLocale(locale);
            }
        }
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     *
     * @param filePath The file to load.
     */
    private static void createAndShowGUI(String filePath) {
        try {
            Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                public void uncaughtException(Thread t, final Throwable e) {
                    SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            handleException(e);
                        }
                    });
                }
            });
            configureLookAndFeel();
            IModel model = new Modellus.Core.Model.Model("Model");
            modellusFrame = new ModellusFrame(model, new StatusBar(model), new AnimatorInternalFrame(model), new Chart2InternalFrame(model), new TableInternalFrame(model), new EditorInternalFrame(model), new NotesInternalFrame(model), new TableExampleFrame(model));
            modellusFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
            modellusFrame.setVisible(true);
            if (!filePath.equals("")) {
                modellusFrame._modellusMainPanel.load(filePath);
            }
        }
        catch (final Exception e) {
            handleException(e);
        }
    }

    static private void handleException(Throwable e) {
        System.out.println(e.toString());
        e.printStackTrace();
        copyToClipboard(e);
        String errorMessage = "A critical error has occured. The details have been copied to the clipboard.";
        String windowTitle = "Exception Occurred";
        int messageType = JOptionPane.ERROR_MESSAGE;
        String options[] = new String[]{"OK"};
        String defaultOption = "OK";
        int choosenOption = JOptionPane.showOptionDialog(findActiveFrame(), errorMessage, windowTitle, -1, messageType, null, options, defaultOption);
        if (choosenOption == 1)
            System.exit(-1);
    }

    private static void configureLookAndFeel() {
        JDialog.setDefaultLookAndFeelDecorated(true);
        JFrame.setDefaultLookAndFeelDecorated(true);
        String skinQualifiedName = loadSkinFromFile();
        if (skinQualifiedName == null)
            SubstanceLookAndFeel.setSkin(new ModellusSkin());
        else
            SubstanceLookAndFeel.setSkin(skinQualifiedName);

    }

    private static String loadSkinFromFile() {
        if (ModellusConfigurationDocument != null) {
            Node localeSettingsNode = PersistableHelper.loadHeader(ModellusConfigurationDocument.getFirstChild(), "SkinInformation");
            if (localeSettingsNode != null)
                return PersistableHelper.loadString(localeSettingsNode, "SkinName");
        }
        return null;
    }

    private static void copyToClipboard(Throwable e) {
        Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(e.toString());
        stringBuilder.append("\n\n");
        StackTraceElement[] stackTrace = e.getStackTrace();
        for (StackTraceElement trace : stackTrace) {
            stringBuilder.append("\tat ");
            stringBuilder.append(trace);
            stringBuilder.append("\n");
        }
        StringSelection contents = new StringSelection(stringBuilder.toString());
        clipboard.setContents(contents, null);
    }

    private static Frame findActiveFrame() {
        Frame[] frames = JFrame.getFrames();
        for (Frame frame : frames)
            if (frame.isVisible())
                return frame;
        return null;
    }

    public IPersister getPersister() {
        return new MainPersister(getModellusMainPanel(), "Main");
    }

    public static void main(String[] args) {
        final String filePath = args.length == 1 ? args[0] : "";
        loadInitialConfigurationFile();
        loadLocaleSettingsConfiguration();
        NEW_DOCUMENT_TITLE = TITLE_ON_TITLEBAR + " - " + LocaleInformation.instance().getBundle("Modellus/WinUI/titles").getString("New_Document_Title");
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI(filePath);
            }
        });
    }

    private static void loadInitialConfigurationFile() {
        XmlUtils xmlUtils = new XmlUtils();
        ModellusConfigurationDocument = xmlUtils.loadDocument(MODELLUS_CONFIGURATION_FILE);
    }
}
